package logic.ships;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

import logic.common.team.Team;
import logic.nodes.SlotNode;
import logic.nodes.collision.DummyType;
import logic.nodes.nodeSettings.TeamNodeSettings;
import logic.ships.equipmentListener.EquipmentListener;
import logic.ships.equipmentListener.WeaponChangedEvent;
import logic.ships.mothership.MotherShip;
import logic.weapons.Weapon;
import logic.weapons.WeaponFire;
import logic.weapons.WeaponSlot;
import logic.weapons.WeaponType;
import main.InitGame;
import main.listeners.ListenerContainer;

import com.jme.math.Vector3f;
import com.jme.scene.Node;

public abstract class Ship extends SlotNode {
	
	private static final long serialVersionUID = 1L;
	
	protected HashMap<WeaponType, List<Weapon>> weapons;
	protected HashMap<WeaponType, List<WeaponSlot>> weaponSlots;
	protected WeaponSlotsCreator slotCreator;
	
	protected ListenerContainer equipmentListeners;
	
	protected Node front, leftWing, rightWing, bottom;
	
	public Ship(String modelPath, String title, Node model, TeamNodeSettings properties, Team team) {
		this(modelPath, title, model, properties, team, DummyType.None);
	}
	
	public Ship(String modelPath, String title, Node model, TeamNodeSettings properties, Team team, 
			DummyType dummyType) {
		super(modelPath, title, model, properties, team, dummyType);
		
		setUpNodes();
		
		weapons = new HashMap<WeaponType, List<Weapon>>();
		for(WeaponType type : WeaponType.values()) {
			weapons.put(type, new ArrayList<Weapon>());
		}
		
		slotCreator = new WeaponSlotsCreator(this);
		weaponSlots = slotCreator.getWeaponSlots();
		
		equipmentListeners = new ListenerContainer(EquipmentListener.class);
	}
	
	@Override
	protected void applyDummyType() {
		super.applyDummyType();
		
		if(InitGame.get().isServer() && dummyType == DummyType.None && !(this instanceof MotherShip)) {
			InitGame.get().getServerState().registerNewShip(this);
			assert(getID() != 0);
		}
	}
	
	public void addEquipmentListener(EquipmentListener l) { equipmentListeners.addListener(l); }
	
	public void removeEquipmentListener(EquipmentListener l) { equipmentListeners.removeListener(l); }
	
	@Override
	public void attachModel() {
		super.attachModel();
		
		if(weapons == null) return;
		for(List<Weapon> weaponList : weapons.values()) {
			for(Weapon w : weaponList) {
				w.attachModel();
			}
		}
	}
	
	@Override
	public void detachModel() {
		super.detachModel();
		
		if(weapons == null) return;
		for(List<Weapon> weaponList : weapons.values()) {
			for(Weapon w : weaponList) {
				w.detachModel();
			}
		}
	}
	
	private void setUpNodes() {
		float length = 30f;
		
		front = new Node("Front");
		front.getLocalTranslation().set(new Vector3f(0f, 0f, length));
		attachChild(front);
		
		leftWing = new Node("Left Wing");
		leftWing.getLocalTranslation().set(new Vector3f(length, 0f, 0f));
		attachChild(leftWing);
		
		rightWing = new Node("Right Wing");
		rightWing.getLocalTranslation().set(new Vector3f(-length, 0f, 0f));
		attachChild(rightWing);
		
		bottom = new Node("Bottom");
		bottom.getLocalTranslation().set(new Vector3f(0f, -length, 0f));
		attachChild(bottom);
	}
	
	@Override
	public void restore() {
		super.restore();
		changeEnergy(getTeamNodeSettings().getMaxEnergy());
	}
	
	public void addWeapon(Weapon weapon) {
		addWeapon(weapon, true);
	}
	
	public void addWeapon(Weapon weapon, boolean add) {
		assert(weapon != null);
		
		WeaponType type = weapon.getWeaponType();
		List<WeaponSlot> slots = weaponSlots.get(type);
		
		if(slots != null && !slots.isEmpty()) {
			int weaponSize = weapons.get(type).size();
			
			if(slots.size() > weaponSize) {
				WeaponSlot slot = slots.get(weaponSize);
				
				if(slot != null) {
					if(add) {
						attachChild(weapon);
						slot.setWeapon(weapon);
						weapons.get(type).add(weapon);
						
						equipmentListeners.callMethod(EquipmentListener.WEAPONADDED, new WeaponChangedEvent(weapon));
						
						if(InitGame.get().isServer()) InitGame.get().getNetworkState().registerNewWeapon(weapon);
					} else InitGame.get().getNetworkState().registerNewWeapon(weapon);
				}
			}
		}
	}
	
	public void enableWeapons(WeaponType type, boolean enable) {
		List<Weapon> list = weapons.get(type);
		for(Weapon w : list) {
			w.setEnabled(enable);
		}
	}
	
	public void fireWeapons(Vector3f dir, WeaponType type) {
		List<Weapon> weaponList = weapons.get(type);

		for(Weapon w : weaponList) {
			WeaponFire fire = w.fire(dir);
			if(fire != null) ingameState.addNode(fire);
		}
	}
	
	public List<Weapon> getWeapons(WeaponType type) {
		return weapons.get(type);
	}
	
	public HashMap<WeaponType, List<Weapon>> getAllWeapons() { return weapons; }
	
	public void removeWeapon(Weapon weapon) {
		assert(weapon != null);
		
		weapons.get(weapon.getWeaponType()).remove(weapon);
		detachChild(weapon);
		
		if(dummyType == DummyType.None) InitGame.get().getNetworkState().unregisterWeapon(weapon);
		
		equipmentListeners.callMethod(EquipmentListener.WEAPONREMOVED, new WeaponChangedEvent(weapon));
	}
	
	public List<WeaponSlot> getWeaponSlots(WeaponType type) { return weaponSlots.get(type); }
	
	public int getWeapontSlotsNumber(WeaponType type) { return getWeaponSlots(type).size(); }
	
	public Vector3f getFrontDirection() { return getFrontLocation().subtract(getLocalTranslation()); }
	
	@Override
	public Vector3f getUpVector() { return getBottomDirection().negate().normalize(); }
	
	public Vector3f getBottomDirection() { return bottom.getWorldTranslation().subtract(getLocalTranslation()); }
	
	public Vector3f getLeftDirection() {  return leftWing.getWorldTranslation().subtract(getLocalTranslation()); }
	
	public Vector3f getRightDirection() {  return rightWing.getWorldTranslation().subtract(getLocalTranslation()); }
	
	public Vector3f getFrontLocation() { return front.getWorldTranslation(); }
		
	@Override
	public void updateTextureQuality() {
		super.updateTextureQuality();
		
		if(weapons == null) return;
		for(List<Weapon> weaponList : weapons.values()) {
			for(Weapon w : weaponList) {
				w.updateTextureQuality();
			}
		}
	}
	
	@Override
	public void updateGeometryQuality() {
		super.updateGeometryQuality();
		
		if(weapons == null) return;
		for(List<Weapon> weaponList : weapons.values()) {
			for(Weapon w : weaponList) {
				w.updateGeometryQuality();
			}
		}
	}
	
	@Override
	public ListenerContainer getListenerContainer(Class<?> listenerClass) {
		if(listenerClass == EquipmentListener.class) return equipmentListeners;
		return super.getListenerContainer(listenerClass);
	}
}